/*
** talker.c -- a datagram "client" demo
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <ctime>
#include <sys/time.h>
#include <fcntl.h>

#include <iostream>
#include <vector>
using namespace std;


#define SERVERPORT 9998	// the port users will be connecting to
int resubmissions = 0; // global var for the resubmission counter
int ack_num = 0; // global var for the number of acks recieved

class msg_time
{
	public:
		time_t stime;
		int msg_number;
		bool set;
		int resubs;
		msg_time();
};

msg_time::msg_time()
{
	stime = time(NULL);
	msg_number = -1;
	set = false; // Set to true when used
	resubs = 0; // number of resubmissions
}


bool checkTime(time_t start, time_t end){
        double difference;
        difference = difftime(end, start);
        if(difference <= 1)
                return 1;
        else
                return 0;
}

// This function checks the vector of current messages & modifies them
int CHECKVEC(vector<msg_time> &alpha, int sockfd, struct sockaddr_in their_addr, int pipe_num)
{
	//cout << "In checkvec" << endl;
	int count = 0;
	time_t now = time(NULL);
	bool valid;
	char msg[10];
	int blah;

	for(count; count < alpha.size(); count++)
	{
		valid = checkTime(alpha[count].stime, now);
		// if the resub count is too big, remove it.
		if(alpha[count].resubs >= 9)
		{
			pipe_num--;
			ack_num++;
			if(!alpha[count].set)
			{
				alpha[count].set = true;
			}
			
		}
		// If the time is not less than or equal to a second.
		else if(!valid && (alpha[count].resubs < 9) )
		{
			cout << "Resubbing" << endl;
			blah = sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr *)&their_addr, sizeof their_addr);
			alpha[count].stime = time(NULL); // reset the timer.
			alpha[count].resubs++; // increment the descriminator
			resubmissions++; // increment for total number of resubs.
			//pipe_num++; // This would make the pipe size run-away i think
		}
		
	}

	return pipe_num;

}// end CHECKVEC

// This function should clear out a confirmed ack, or normalize if erroneous ack
int CLEANVEC(vector<msg_time> &alpha, char* my_buf, int ack_num)
{

	cout << "In cleanvec" << endl;
	int my_num = atoi(my_buf);
	vector<msg_time>::iterator iter;
	iter = alpha.begin();
	msg_time mytime;
	bool redund = false;

	for( iter = alpha.begin(); iter != alpha.end(); iter++ )
	{
		cout << "                 in for : ";
		mytime = *iter;
		if(mytime.msg_number == my_num)
		{
			cout << "              in num if ";
			alpha.erase(iter); // remove this from the holder
			//iter--; // move back to ensure no skipping ---- Not sure if this is necessary
			ack_num++;
			redund = true;
			
		}
		// If the flag was tripped in CHECKVEC
		if(mytime.set)
		{
			cout << "                 in flag if ";
			alpha.erase(iter);
			//iter--; //--- not sure if this is necessary
		}
		cout << endl;
	}
	
		

	return ack_num;
		
}// end CLEANVEC

int main(int argc, char *argv[])
{
	int sockfd, my_rand, percent, SWS, msg_to_send, count, test_rand, pipe_num;
	struct sockaddr_in their_addr; // connector's address information
	struct hostent *he;
	int numbytes;
	srand(time(NULL)); // Randomly seed the randomn number func
	char msg[1024];
	char buf[1024]; // ack buffer
	struct timeval start, stop; // timeval used by gettimeofday() for the timeout
	socklen_t addr_len; // used for recieving acks
	vector<msg_time> timers;
	msg_time my_message; // values pre-set by constructor

	//cout << "My message number is " << my_message.msg_number << endl;

	//Get the right number of arguments
	if (argc != 5) {
		fprintf(stderr,"usage: talker, hostname, SWS, percent, message number\n");
		exit(1);
	}

	// Get information about server
	if ((he=gethostbyname(argv[1])) == NULL) { 
		perror("gethostbyname");
		exit(1);
	}

	// Socket & check
	if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
		perror("socket");
		exit(1);
	}
	fcntl(sockfd, F_SETFL, O_NONBLOCK);// setup the client socket as non-blocking

	// get the SWS and check it for accuracy
	SWS = atoi(argv[2]);

	if(SWS >= 10 || SWS < 1)
	{
		fprintf(stderr, "Incorrect Send Window Size, range is from 1-10.\n");
		exit(1);
	}

	// Get the percent to use and check it for accuracy
	percent = atoi(argv[3]);
	if(percent < 0 || percent > 100)
	{
		fprintf(stderr, "Invalid percent number range from 1-100.\n");
		exit(1);
	}
	// Get the number of messages to be sent and ensure greater than 0
	msg_to_send = atoi(argv[4]);
	if(msg_to_send <= 0 || msg_to_send >= 10000000)
	{
		fprintf(stderr, "Invalid number of messages chosen, too few or more than 10 million.\n");
		exit(1);
	}

	// Set protocol up, and addressing
	their_addr.sin_family = AF_INET;	 // host byte order
	their_addr.sin_port = htons(SERVERPORT); // short, network byte order
	their_addr.sin_addr = *((struct in_addr *)he->h_addr);
	addr_len = sizeof their_addr;
	memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);


	pipe_num = 0;
	int test_counter = 0;
//	int ack_num = 0; ---- This is now a global var;
	count = 0;
	int sent_msgs = 0;
	while(ack_num < msg_to_send)
	{

		// If the pipe is not full, push something into it
		if(pipe_num < SWS)
		{
			// Create a var for the vector
			my_message.stime = time(NULL);
			my_message.msg_number = sent_msgs;
			my_message.set = false;
			// Push it on, will be deleted later
			timers.push_back(my_message);
			
			// Simulate dropping & adjust pipe variables
			test_rand = (rand() % 100) + 1;
			pipe_num++;
			sprintf(msg,"%d", sent_msgs);
			sent_msgs++;

			if(test_rand <= percent) 
			{	
				sendto(sockfd, msg, strlen(msg), 0,(struct sockaddr *)&their_addr, sizeof their_addr);
				cout << "Sent message  :  " << msg << endl;
				
			}

		}
		// If the pipe is full, run the SWS protocol stuff
		else
		{
			
			// Note that this recvfrom is non-blocking
			numbytes = recvfrom(sockfd, buf, 1023 , 0,(struct sockaddr *)&their_addr,&addr_len);
			if(numbytes == -1)
			{
				//Nothing read, nothing done
			}
			else
			{		
				// Need another check to make sure proper ack
				ack_num = CLEANVEC(timers, buf, ack_num);
				cout << "Ack recv -> " << ack_num << endl;
				pipe_num--;
			}

			pipe_num = CHECKVEC(timers, sockfd, their_addr, pipe_num);


		}
		
			
		
	}


	cout << "Finished. Total Submissions => " << resubmissions + msg_to_send << " Resubmissions => " << resubmissions << "." << endl;
	return 0;
}
